#region File Description
//-----------------------------------------------------------------------------
// AudioManager.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using System;
using Microsoft.Xna.Framework.Content;
using UnityEngine.Gameplay.Sound;
using UnityEngine.Components;
#endregion

namespace UnityEngine.Audio
{
    /// <summary>
    /// Audio manager keeps track of what 3D sounds are playing, updating
    /// their settings as the camera and entities move around the world, and
    /// automatically disposing sound effect instances after they finish playing.
    /// </summary>
    public class AudioManager : IDisposable
    {
        private static AudioManager _Instance = null;

        public static AudioManager Instance
        {
            get
            {
                if (_Instance == null)
                    _Instance = new AudioManager();
                return _Instance;
            }
        }

        public static void SingletonDispose()
        {
            if (_Instance != null)
            {
                _Instance.Dispose();
                _Instance = null;
            }
        }

        private ContentManager _Content;
        public void SetContent(ContentManager content)
        {
            this._Content = content;
            SoundEffect.DistanceScale = 2000;
            SoundEffect.DopplerScale = 0.1f;
        }


        // The listener describes the ear which is hearing 3D sounds.
        // This is usually set to match the camera.
        public AudioListener Listener
        {
            get { return listener; }
        }

        AudioListener listener = new AudioListener();


        // The emitter describes an entity which is making a 3D sound.
        AudioEmitter emitter = new AudioEmitter();


        // Store all the sound effects that are available to be played.
        Dictionary<string, SoundEffect> soundEffects = new Dictionary<string, SoundEffect>();


        // Keep track of all the 3D sounds that are currently playing.
        List<ActiveSound> activeSounds = new List<ActiveSound>();


        private AudioManager()
        { }

        public void Dispose()
        {
            this.Dispose(true);
        }

        public virtual void Dispose(bool disposing)
        {

            if (disposing)
            {
                foreach (SoundEffect soundEffect in soundEffects.Values)
                {
                    soundEffect.Dispose();
                }

                soundEffects.Clear();
            }
        }

        public void Update(Camera camera)
        {
            this.Listener.Position = camera.Position;
            this.Listener.Forward = camera.LookAt - camera.Position;
            this.Listener.Up = camera.Up;
            this.Listener.Velocity = Vector3.Zero;
        }

        public void Update(GameTime gameTime)
        {
            // Loop over all the currently playing 3D sounds.
            int index = 0;

            while (index < activeSounds.Count)
            {
                ActiveSound activeSound = activeSounds[index];

                if (activeSound.Instance.State == SoundState.Stopped)
                {
                    // If the sound has stopped playing, dispose it.
                    activeSound.Instance.Dispose();

                    // Remove it from the active list.
                    activeSounds.RemoveAt(index);
                }
                else
                {
                    // If the sound is still playing, update its 3D settings.
                    Apply3D(activeSound);

                    index++;
                }
            }
        }


        /// <summary>
        /// Triggers a new 3D sound.
        /// </summary>
        public SoundEffectInstance Play3DSound(string soundName, bool isLooped, SoundEmitter emitter)
        {
            if (soundEffects.ContainsKey(soundName) == false)
                soundEffects.Add(soundName, this._Content.Load<SoundEffect>(soundName));

            ActiveSound activeSound = new ActiveSound();

            // Fill in the instance and emitter fields.
            activeSound.Instance = soundEffects[soundName].CreateInstance();
            activeSound.Instance.IsLooped = isLooped;

            activeSound.Emitter = emitter;

            // Set the 3D position of this sound, and then play it.
            Apply3D(activeSound);

            activeSound.Instance.Play();

            // Remember that this sound is now active.
            activeSounds.Add(activeSound);

            return activeSound.Instance;
        }


        /// <summary>
        /// Updates the position and velocity settings of a 3D sound.
        /// </summary>
        private void Apply3D(ActiveSound activeSound)
        {
            emitter.Position = activeSound.Emitter.Position;
            emitter.Forward = activeSound.Emitter.Forward;
            emitter.Up = activeSound.Emitter.Up;
            emitter.Velocity = activeSound.Emitter.Velocity;

            activeSound.Instance.Apply3D(listener, emitter);
        }


        /// <summary>
        /// Internal helper class for keeping track of an active 3D sound,
        /// and remembering which emitter object it is attached to.
        /// </summary>
        private class ActiveSound
        {
            public SoundEffectInstance Instance;
            public SoundEmitter Emitter;
        }
    }
}
